`define DELAY(N, clk) begin \
	repeat(N) @(posedge clk);\
	#1ps;\
end

import hand_dp_ram_rd_ctrl_pkg::*;

module testbench();

//-------------------------------------{{{common cfg
timeunit 1ns;
timeprecision 1ps;
initial $timeformat(-9,3,"ns",6);

string tc_name;
int tc_seed;

initial begin
    if(!$value$plusargs("tc_name=%s", tc_name)) $error("no tc_name!");
    else $display("tc name = %0s", tc_name);
    if(!$value$plusargs("ntb_random_seed=%0d", tc_seed)) $error("no tc_seed");
    else $display("tc seed = %0d", tc_seed);
end
//-------------------------------------}}}

//-------------------------------------{{{parameter declare
parameter DATA_W = 8;
parameter INFO_W = 8;
parameter RAM_DL = 2;
parameter DEPTH  = 16;
//-------------------------------------}}}

//-------------------------------------{{{signal declare
logic  clk;
logic  rst_n;
logic  ar_valid;
logic  ar_ready;
logic [$clog2(DEPTH) -1:0] ar_addr;
logic [INFO_W        -1:0] ar_info;
logic  r_valid;
logic  r_ready;
logic [DATA_W        -1:0] r_data;
logic [INFO_W        -1:0] r_info;
logic  ram_renc;
logic [$clog2(DEPTH) -1:0] ram_raddr;
logic [DATA_W        -1:0] ram_r_data;
//-------------------------------------}}}

//-------------------------------------{{{clk/rst cfg
initial forever #5ns clk = ~clk;
initial begin
    rst_n = 1'b0;
	`DELAY(30, clk);
	rst_n = 1'b1;
end
initial begin
    #100000ns $finish;
end
//-------------------------------------}}}

//-------------------------------------{{{valid sig assign
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)begin
        ar_valid <= 0;
    end
    else if(ar_ready || ~ar_valid)begin
        ar_valid <= $urandom;
    end
end

//-------------------------------------}}}

//-------------------------------------{{{ready sig assign
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)begin
        r_ready <= 0;
    end
    else begin
        r_ready <= $urandom;
    end
end

//-------------------------------------}}}

//-------------------------------------{{{data  sig assign
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)begin
        ar_addr <= 'x;
    end
    else if(ar_valid && ar_ready)begin
        ar_addr <= $urandom;
    end
    else if(ar_valid == 0)begin
        ar_addr <= $urandom;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(~rst_n)begin
        ar_info <= 'x;
    end
    else if(ar_valid == 0)begin
        ar_info <= $urandom;
    end
end

//-------------------------------------}}}

//-------------------------------------{{{other sig assign
initial begin
    `DELAY(50, clk);
end

//-------------------------------------}}}

//-------------------------------------{{{rtl inst
hand_dp_ram_rd_ctrl #(
    .DATA_W(DATA_W),
    .INFO_W(INFO_W),
    .RAM_DL(RAM_DL)) 
u_hand_dp_ram_rd_ctrl(
    .clk(clk),
    .rst_n(rst_n),
    .ar_valid(ar_valid),
    .ar_ready(ar_ready),
    .ar_addr(ar_addr),
    .ar_info(ar_info),
    .r_valid(r_valid),
    .r_ready(r_ready),
    .r_data(r_data),
    .r_info(r_info),
    .ram_renc(ram_renc),
    .ram_raddr(ram_raddr),
    .ram_r_data(ram_r_data)
);

dual_port_RAM_2cyc #(.DEPTH(DEPTH), .WIDTH(DATA_W))
u_ram(
	 .wclk  (clk),
	 .wenc  ('0),
	 .waddr ('0),
	 .wdata ('0),
	 .rclk  (clk),
	 .renc  (ram_renc),
	 .raddr (ram_raddr),
	 .rdata (ram_r_data)
);

initial begin
    foreach(u_ram.RAM_MEM[i]) begin
        u_ram.RAM_MEM[i] = i;
    end
end
//-------------------------------------}}}

//-------------------------------------{{{auto_verification
task in_queue_gain();
  while(1)begin
    @(negedge clk);
    if(ar_valid && ar_ready)begin
      ar_valid_struct ar_valid_dat;
      ar_valid_dat.ar_addr = ar_addr;
      ar_valid_dat.ar_info = ar_info;
      ar_valid_bus_q.push_back(ar_valid_dat);
    end//if-end 
  end//while-end 
endtask: in_queue_gain

task out_queue_gain();
  while(1)begin
    @(negedge clk);
    if(r_valid && r_ready)begin
      r_valid_struct r_valid_dat;
      r_valid_dat.r_data = r_data;
      r_valid_dat.r_info = r_info;
      r_valid_bus_q.push_back(r_valid_dat);
    end
  end//while-end 
endtask: out_queue_gain

task rm_queue_gain();
  ar_valid_struct ar_valid_dat;
  r_valid_struct  r_valid_dat;
  while(1)begin
    wait(ar_valid_bus_q.size > 0);
    ar_valid_dat = ar_valid_bus_q.pop_front();
    r_valid_dat.r_data = ar_valid_dat.ar_addr;
    r_valid_dat.r_info = ar_valid_dat.ar_info;
    rm_q.push_back(r_valid_dat);
  end
endtask: rm_queue_gain

task queue_check();
  while(1)begin
    r_valid_struct rm_data;
    r_valid_struct dual_data;
    wait(r_valid_bus_q.size() > 0);
    dual_data = r_valid_bus_q.pop_front();
    if(rm_q.size() == 0) begin
      $display("dual_data = %0p, rm_queue.size = 0", dual_data);
      error_cnt += 1;
    end
    else begin
      rm_data = rm_q.pop_front();
      if(dual_data != rm_data)begin
        error_cnt += 1;
        $display("dual_data(%0p) != rm_data(%0p) at %t", dual_data, rm_data, $realtime);
      end
      else begin
        //$display("dual_data(%0p) == rm_data(%0p) at %t", dual_data, rm_data, $realtime);
      end
    end
    if(error_cnt >= ERROR_DEBUG_CNT) begin
      $display("Check Error!!!");
      $finish;
    end
  end
endtask: queue_check

initial begin
  fork
    in_queue_gain();
    out_queue_gain();
    rm_queue_gain();
    if(check_en == 1) queue_check();
  join_none
end

endmodule
